package net.orange.console;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import net.orange.console.model.analyzer.AnalyseDimension;
import net.orange.console.model.analyzer.AnalyseFilter;
import net.orange.console.model.analyzer.AnalyseTable;
import net.orange.console.model.analyzer.CalculationMethod;
import net.orange.console.model.analyzer.Calculator;
import net.orange.console.model.dataset.ConsoleData;
import net.orange.console.model.dataset.ConsoleDataSet;
import net.orange.console.model.dataset.ConsoleMetaData;
import net.orange.console.model.dataset.ConsoleRow;

/**
 * The html analyse table console writer which implement the AnalyseTableWriter
 * interface, it can export the analyze table to html.
 * 
 */
public class HtmlAnalyseTableWriter implements AnalyseTableWriter {
	/*
	 * (non-Javadoc)
	 * 
	 * @see net.orange.console.AnalyseTableWriter#write(java.lang.String,
	 *      net.orange.console.model.analyzer.AnalyseTable)
	 */
	public String getTableHtml(String url, AnalyseTable analyseTable)
			throws Exception {
		if (analyseTable == null)
			return "<b>No data found</b>";

		StringBuffer html = new StringBuffer();

		html.append("<table>");
		html.append(getColumnSetHtml(analyseTable));
		html.append(getRowSetHtml(analyseTable));
		html.append("</table>");

		return html.toString();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.orange.console.AnalyseTableWriter#getFilterHtml(java.lang.String,
	 *      java.util.Collection,
	 *      net.orange.console.model.dataset.ConsoleDataSet)
	 */
	public String getFilterHtml(String url, Collection<AnalyseFilter> filters,
			Collection<ConsoleData> filtersData, ConsoleDataSet dataset) {
		StringBuffer html = new StringBuffer();

		html.append("<table >");

		Object[] filterAry = filters.toArray();
		Object[] filterDataAry = filtersData.toArray();

		for (int i = 0; i < filterAry.length; ++i) {
			AnalyseFilter filter = (AnalyseFilter) filterAry[i];
			ConsoleData filterData = (ConsoleData) filterDataAry[i];

			html.append("<tr>");

			html.append("<td class='content-detail-title-right' nowrap> ");
			html.append(filter.getMetaData().getColumnName() + ":");
			html.append("</td>");

			html.append("<td align='left' class='content-detail' nowrap>");
			html.append("<select name='filter_"
					+ filter.getMetaData().getColumnName()
					+ "' width='' onchange='document.forms[0].submit();'>");
			html.append("<option value=''></option>");

			html.append(getFilterOptionHtml(filter, filterData, dataset));

			html.append("</select>");

			html.append("</td>");

			html.append("</tr>");
		}
		html.append("</table>");

		return html.toString();
	}

	/**
	 * Get the analyse table column header html
	 * 
	 * @param analyseTable
	 *            The target analyse table.
	 * @return The analyse table column head html.
	 */
	private String getColumnSetHtml(AnalyseTable analyseTable) {

		StringBuffer buffer = new StringBuffer();
		int i = 1;

		for (AnalyseDimension column : analyseTable.getColumnSet()) {
			buffer.append("<tr class='content-detail-title'>");

			List<String> keySet = column.getDatasKeyIterator();

			if (i == 1) {
				buffer.append("<td nowrap rowspan='"
						+ analyseTable.getColumnSet().size() + "' colspan='"
						+ analyseTable.getRowSet().size() + "'>&nbsp;</td>");
			}

			for (String key : keySet) {
				buffer.append("<td colspan='"
						+ getColumnSpan(key, analyseTable) + "' nowrap>");

				buffer.append(getKeyLabel(key));

				buffer.append("</td>");
			}

			buffer.append("</tr>");
			++i;
		}

		return buffer.toString();
	}

	/**
	 * Get the column span.
	 * 
	 * @param analyseColumnKey
	 *            The analyse column key.
	 * @param analyseTable
	 *            The analyse table.
	 * @return The The analyse column span count.
	 */
	private int getColumnSpan(String analyseColumnKey, AnalyseTable analyseTable) {

		Object[] columnSet = analyseTable.getColumnSet().toArray();

		AnalyseDimension lastAnalyseColumn = (AnalyseDimension) columnSet[columnSet.length - 1];

		Collection<String> keySet = lastAnalyseColumn.getDatas().keySet();

		int count = 0;

		for (String key : keySet) {
			if (key.startsWith(analyseColumnKey
					+ AnalyseDimension.KEY_KEY_SEPARATOR))
				count++;
		}

		return count;
	}

	/**
	 * Get the key label.
	 * 
	 * @param key
	 *            The whole key
	 * @return The key label
	 */
	private String getKeyLabel(String key) {
		String[] keys = key.split(AnalyseDimension.KEY_KEY_SEPARATOR);
		return keys[keys.length - 1];
	}

	/**
	 * Get the analyse table row html
	 * 
	 * @param analyseTable
	 *            The target analyse table.
	 * @return The analyse table column head html.
	 */
	private String getRowSetHtml(AnalyseTable analyseTable) {
		StringBuffer buffer = new StringBuffer();

		Object[] rowDimesions = analyseTable.getRowSet().toArray();
		AnalyseDimension lastRowDimension = (AnalyseDimension) rowDimesions[rowDimesions.length - 1];

		Object[] columnDimesions = analyseTable.getColumnSet().toArray();
		AnalyseDimension lastColumnDimension = (AnalyseDimension) columnDimesions[columnDimesions.length - 1];

		Map<String, Collection<ConsoleRow>> resultDatas = lastRowDimension
				.getCrossDatas(lastColumnDimension);

		Collection outputValue = new ArrayList();

		for (String lastRowKey : lastRowDimension.getDatasKeyIterator()) {
			buffer.append("<tr>");

			// Output the row deminsions
			String[] keys = lastRowKey
					.split(AnalyseDimension.KEY_KEY_SEPARATOR);

			for (int i = 0; i < keys.length; ++i) {
				String currentKey = "";

				for (int j = 0; j <= i; ++j) {
					currentKey = (currentKey.length() > 0) ? currentKey
							+ AnalyseDimension.KEY_KEY_SEPARATOR + keys[j]
							: keys[j];
				}

				if (!outputValue.contains(currentKey)) {
					buffer
							.append("<td nowrap class='content-detail-title' rowspan='"
									+ getRowSpan(currentKey, analyseTable)
									+ "'>" + getKeyLabel(currentKey) + "</td>");

					outputValue.add(currentKey);
				}
			}

			// Output the calculation result.
			for (String lastColumnKey : lastColumnDimension
					.getDatasKeyIterator()) {

				String resultKey = lastRowKey
						+ AnalyseDimension.DIM_DIM_SEPARATOR + lastColumnKey;

				Collection datas = resultDatas.get(resultKey);

				buffer.append(getCalculationHtml(datas, analyseTable
						.getCalculationField(), analyseTable
						.getCalculationMethod()));
			}

		}
		buffer.append("</tr>");

		return buffer.toString();
	}

	/**
	 * Get the column span.
	 * 
	 * @param analyseRowKey
	 *            The analyse column key.
	 * @param analyseTable
	 *            The analyse table.
	 * @return The The analyse column span count.
	 */
	private int getRowSpan(String analyseRowKey, AnalyseTable analyseTable) {

		Object[] rowSet = analyseTable.getRowSet().toArray();

		AnalyseDimension lastAnalyseColumn = (AnalyseDimension) rowSet[rowSet.length - 1];

		Collection<String> keySet = lastAnalyseColumn.getDatas().keySet();

		int count = 0;

		for (String key : keySet) {
			if (key.startsWith(analyseRowKey
					+ AnalyseDimension.KEY_KEY_SEPARATOR))
				count++;
		}

		return count;
	}

	/**
	 * Get the calculation result html
	 * 
	 * @param datas
	 *            The result row
	 * @param calculationField
	 *            The calculation field meta data.
	 * @param method
	 *            The calculation method
	 * @return The result in html
	 */
	private String getCalculationHtml(Collection<ConsoleRow> datas,
			ConsoleMetaData calculationField, CalculationMethod method) {
		StringBuffer buffer = new StringBuffer();
		Calculator calculator = new Calculator();

		buffer.append("<td class='content-data' align='right'>");

		if (method == CalculationMethod.DISTINCT) {
			buffer.append("<table>");

			if (datas != null && datas.size() > 0) {
				for (ConsoleRow row : datas) {
					ConsoleData data = row.getData(calculationField);
					buffer.append("<tr class='content-detail'>");
					buffer.append("<td align='right'>");
					buffer.append(data != null ? data.getStringValue() : "");
					buffer.append("</td>");
					buffer.append("</tr>");
				}

			}

			buffer.append("</table>");
		} else {
			buffer.append(calculator.compute(datas, method, calculationField));
		}

		buffer.append("</td>");
		return buffer.toString();
	}

	/**
	 * Get the filter option html.
	 * 
	 * @param The
	 *            filter.
	 * @param filterData
	 *            The filter console data.
	 * @param dataset
	 *            The console data set.
	 * @return the filter option html
	 */
	private String getFilterOptionHtml(AnalyseFilter filter,
			ConsoleData filterData, ConsoleDataSet dataset) {
		Collection<String> datas = new ArrayList<String>();
		StringBuffer buffer = new StringBuffer();

		if (dataset != null) {
			for (ConsoleRow row : dataset.getRows()) {
				ConsoleData data = row.getData(filter.getMetaData());

				if (!datas.contains(data.getStringValue()))
					datas.add(data.getStringValue());
			}
		}

		for (String option : datas) {
			if (option != null) {
				buffer.append("<option value='" + option + "' ");

				if (filterData != null
						&& option.equals(filterData.getStringValue()))
					buffer.append("selected");
				buffer.append(">");

				buffer.append(option);

				buffer.append("</option>");
			}
		}

		return buffer.toString();
	}
}
